package com.zmn.oms.business.impl.baidumap;

import com.alibaba.fastjson.JSON;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.map.Point;
import com.zmn.common.utils.math.MathUtil;
import com.zmn.oms.business.interfaces.baidumap.BaiduMapBService;
import com.zmn.oms.model.dto.baidumap.LngLatLocation;
import com.zmn.tapi.common.baidu.GeocodingResult;
import com.zmn.tapi.common.baidu.Location;
import com.zmn.tapi.common.baidu.SuggestionResult;
import com.zmn.tapi.common.baidu.map.DirectionDrivingRequest;
import com.zmn.tapi.common.baidu.map.DirectionDrivingResult;
import com.zmn.tapi.common.baidu.map.RoutematrixRequest;
import com.zmn.tapi.common.baidu.map.RoutematrixResult;
import com.zmn.tapi.dubbo.interfaces.baidu.BaiduMapRemoteService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 类描述：百度地图
 *
 * @author liuying
 * @date 2021/08/25 10:39
 */
@Slf4j
@Service("baiduMapBService")
public class BaiduMapBServiceImpl implements BaiduMapBService {

    @Reference(version = com.zmn.tapi.dubbo.DubboConsts.INTERFACE_VERSION, check = false)
    private BaiduMapRemoteService baiduMapRemoteService;

    @Override
    public LngLatLocation location(String address) {
        if (StringUtils.isBlank(address)) {
            return null;
        }
        LngLatLocation lngLatLocation = null;

        try {
            ResponseDTO<GeocodingResult> responseDTO = baiduMapRemoteService.geocoding(address, null);
            if (responseDTO.isSuccess()) {
                lngLatLocation = Optional.ofNullable(responseDTO.getData())
                        .map(data -> data.getLocation())
                        .filter(location -> location.getLng() != null && location.getLat() != null)
                        .map(location -> new LngLatLocation(location.getLng().toPlainString(), location.getLat().toPlainString()))
                        .orElse(null);
            }

            if (lngLatLocation == null) {
                log.warn("通过地址获取经纬度失败：address=[{}], responseDTO=[{}]", address, JSON.toJSONString(responseDTO));
            }
        } catch (Exception e) {
            log.error(String.format("通过地址获取经纬度失败：%s。 address=[%s]", e.getMessage(), address), e);
        }

        return lngLatLocation;
    }

    /**
     * 通过区域和地址查找可能得地址和经纬度
     *
     * @param region
     * @param address
     * @return
     */
    @Override
    public LngLatLocation suggestion(String region, String address) {

        if (StringUtils.isBlank(region) || StringUtils.isBlank(address)) {
            return null;
        }

        try {
            ResponseDTO<List<SuggestionResult>> responseDTO = baiduMapRemoteService.placeSuggestion(region, address, Boolean.TRUE);
            log.info("通过地址查找匹配失败：region=[{}]，address=[{}], responseDTO=[{}]", address, JSON.toJSONString(responseDTO));
            if (!responseDTO.isSuccess() || CollectionUtils.isEmpty(responseDTO.getData())) {
                return null;
            }

            SuggestionResult suggestionResult = responseDTO.getData().get(0);
            if (Objects.isNull(suggestionResult) || Objects.isNull(suggestionResult.getLocation())) {
                return null;
            }
            Location location = suggestionResult.getLocation();
            if (Objects.isNull(location.getLat()) || Objects.isNull(location.getLng())) {
                return null;
            }

            LngLatLocation lngLatLocation = new LngLatLocation(location.getLng().toPlainString(), location.getLat().toPlainString(), suggestionResult.getName());
            return lngLatLocation;
        } catch (Exception e) {
            log.error(String.format("通过地址查找匹配失败：%s。 region=[{}]，address=[%s]", e.getMessage(), address), e);
        }

        return null;
    }

    @Override
    public Integer getNavigationDistance(Point originPoint, Point descPoint) {

        try {
            DirectionDrivingRequest request = new DirectionDrivingRequest();
            // 不走高速
            request.setTactics(3);
            request.setOrigin(String.format("%s,%s", originPoint.getLat(), originPoint.getLng()));
            request.setDestination(String.format("%s,%s", descPoint.getLat(), descPoint.getLng()));
            ResponseDTO<DirectionDrivingResult> directionDrivingResultResponseDTO = baiduMapRemoteService.directionDriving(request);
            DirectionDrivingResult directionDrivingResult = directionDrivingResultResponseDTO.getData();
            if (directionDrivingResult == null) {
                return null;
            }
            List<DirectionDrivingResult.RoutesResult> routesResultList = directionDrivingResult.getRoutes();
            if (CollectionUtils.isEmpty(routesResultList)) {
                return null;
            }

            return routesResultList.get(0).getDistance();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;

        /*
        // 不走高速
        String tactics = "3";
        // 0：默认 3：不走高速 4：高速优先 5：躲避拥堵 6：少收费 7：躲避拥堵&高速优先 8：躲避拥堵&不走高速 9：躲避拥堵&少收费 10：躲避拥堵&不走高速&少收费 11：不走高速&少收费
        String url = String.format("%s?origin=%s,%s&destination=%s,%s&tactics=%s&ak=%s", BaiduMapUtil.DRIVING_URL,
                originPoint.getLat(), originPoint.getLng(), descPoint.getLat(), descPoint.getLng(), tactics, BaiduMapUtil.getAk());
        log.debug("导航距离请求：" + url);

        try {
            String res = MonitorHttpsUtils.get(url);
            JSONObject jsonObject = JSON.parseObject(res);

            // 0：成功 1：服务内部错误 2：参数无效 7：无返回结果
            String successStatus = "0";
            if (successStatus.equals(jsonObject.getString("status"))) {
                Integer distance = jsonObject.getJSONObject("result").getJSONArray("routes").getJSONObject(0).getInteger("distance");
                return distance;
            } else {
                log.error("导航距离返回失败=>" + res);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return null;
         */
    }

    @Override
    public List<Integer> getMultiRidingDistance(Point originPoint, List<Point> descPointList) {
        if (CollectionUtils.isEmpty(descPointList)) {
            return null;
        }

        long startTime = System.currentTimeMillis();

        int maxSize = 50;

        // 由于百度批量算路接口单次调用50个的限制，小于等于50个，直接计算
        if (descPointList.size() <= maxSize) {
            return getMultiRidingDistanceLimit50(originPoint, descPointList);
        }

        // 大于50个，分段计算
        List<Integer> resList = new ArrayList<>(descPointList.size());

        // 最后一个分段大小
        int lastSegmentSize;
        // 余数
        int remainder = descPointList.size() % maxSize;

        if (remainder == 0) {
            lastSegmentSize = maxSize;
        } else {
            lastSegmentSize = remainder;
        }

        // 分段大小
        int segmentSize;

        // 分批调用百度接口
        for (int i = 0; i < descPointList.size(); ) {

            // 最后一个分段
            if (i == descPointList.size() - 1) {
                segmentSize = lastSegmentSize;
            } else {
                segmentSize = maxSize;
            }

            List<Point> ptList = new ArrayList<>(segmentSize);
            for (int j = 0; j < segmentSize && i < descPointList.size(); i++, j++) {
                ptList.add(descPointList.get(i));
            }

            List<Integer> disList = getMultiRidingDistanceLimit50(originPoint, ptList);
            // 调用失败一次即全部失败
            if (disList == null) {
                return null;
            }

            resList.addAll(disList);
        }

        long endTime = System.currentTimeMillis();
        log.info("getMultiRidingDistance times ->{}", (endTime - startTime));

        return resList;
    }

    @Override
    public Double getCornerDistance(double lng1, double lat1, double lng2, double lat2) {
        double s = 0.0;

        do {
            // 位置相同
            if (lng1 == lng2 && lat1 == lat2) {
                break;
            }

            // 经度或者纬度之一相同
            if (lng1 == lng2 || lat1 == lat2) {
                s = MathUtil.GetDistance(lng1, lat1, lng2, lat2);
                break;
            }

            // 经度和纬度都不同，转换为两个拐弯距离之和
            double s1 = MathUtil.GetDistance(lng1, lat1, lng1, lat2);
            double s2 = MathUtil.GetDistance(lng1, lat1, lng2, lat1);

            s = s1 + s2;
        } while (false);

        return s;
    }

    /**
     * 多点获取骑行距离，单位米（限50个）
     *
     * @param originPoint
     * @param descPointList
     * @return
     */
    private List<Integer> getMultiRidingDistanceLimit50(Point originPoint, List<Point> descPointList) {
        if (CollectionUtils.isEmpty(descPointList)) {
            return null;
        }

        try {
            RoutematrixRequest request = new RoutematrixRequest();
            request.setType(3);
            request.setOrigins(String.format("%s,%s", originPoint.getLat(), originPoint.getLng()));
            request.setDestinations(descPointList.stream().map(pt -> String.format("%s,%s", pt.getLat(), pt.getLng()))
                    .collect(Collectors.joining("|")));
            log.info("baiduMapRemoteService#routematrix 入参：{}", JSON.toJSONString(request));
            ResponseDTO<List<RoutematrixResult>> responseDTO = baiduMapRemoteService.routematrix(request);
            List<RoutematrixResult> resultList = responseDTO.getData();
            if (CollectionUtils.isEmpty(resultList)) {
                //log.info("多点导航距离返回结果数量不匹配=>null");
                return null;
            }
            if (resultList.size() < descPointList.size()) {
                log.error("多点导航距离返回结果数量不匹配=>" + resultList.size());
                return null;
            }

            List<Integer> resList = resultList.stream().map(item -> item.getDistance().getValue()).map(Integer::parseInt)
                    .collect(Collectors.toList());
            return resList;
        } catch (Exception ex) {
            log.error("查询多点导航距离异常{}", ex);
        }

        return null;

        /*
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("%s?output=json&origins=%s,%s&destinations=", BaiduMapUtil.MULTI_RIDING_URL, originPoint.getLat(), originPoint.getLng()));
        int i = 0;

        for (Point pt : descPointList) {
            // 百度批量算路接口一次最多处理50个
            if (i >= 50) {
                break;
            }

            if (i > 0) {
                sb.append("%7C");
            }

            sb.append(String.format("%s,%s", pt.getLat(), pt.getLng()));
            i++;
        }

        sb.append(String.format("&ak=%s", BaiduMapUtil.getAk()));
        String url = sb.toString();
        log.debug("多点导航距离请求：" + url);

        try {
            String res = MonitorHttpsUtils.get(url);
            log.debug("多点导航距离请求结果：" + res);
            JSONObject jsonObject = JSON.parseObject(res);

            // 0：成功 其他：错误
            String successStatus = "0";
            if (jsonObject != null && successStatus.equals(jsonObject.getString("status"))) {
                List<Integer> resList = new ArrayList<>(descPointList.size());
                JSONArray array = jsonObject.getJSONArray("result");

                if (array == null) {
                    log.error("多点导航距离返回结果为空");
                    return null;
                }

                if (array.size() < descPointList.size()) {
                    log.error("多点导航距离返回结果数量不匹配=>" + array.size());
                    return null;
                }

                for (i = 0; i < array.size(); i++) {
                    resList.add(array.getJSONObject(i).getJSONObject("distance").getInteger("value"));
                }

                return resList;
            } else {
                log.error("多点导航距离返回失败=>" + res);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

        return null;
        */
    }

}
